package suncertify.gui;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.JTextField;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.PlainDocument;

import suncertify.application.Strings;

/**
 * Provides a text field that allows the entry of numeric digits only. The text field has the following properties:
 * 
 * <ol>
 * <li>The text field is constructed with a parameter that defines the maximum allowed integer value in the text field.</li>
 * <li>The first character must be 1 to 9, followed by 0 or more (0-9) numeric characters.</li>
 * <li>Any characters other than the numeric characters are ignored and will not appear in the text field.</li>
 * <li>The text field can be empty.</li>
 * </ol>
 * 
 */
public class UnsignedIntegerJTextField extends JTextField {

    /**
     * Automatically generated serialization ID.
     */
    private static final long serialVersionUID = 5165L;

    /**
     * Defines the maximum allowed value.
     */
    private int maxValue = Integer.MAX_VALUE;

    /**
     * Constructs the class with the specified <code>columns</code> and <code>maxValue</code> parameters. The
     * <code>columns</code> parameter is passed to the {@link JTextField} constructor, the <code>maxValue</code> is
     * saved in the {@link #maxValue} member variable.
     * 
     * @param columns
     *            See {@link JTextField}.
     * @param maxValue
     *            Positive integer that defines the maximum integer value allowed to be typed in the text field.
     * 
     * @throws IllegalArgumentException
     *             If the <code>maxValue</code> is less than 0.
     */
    public UnsignedIntegerJTextField(int columns, int maxValue) {

	super(columns);

	if (maxValue < 0) {
	    throw new IllegalArgumentException(Strings.MSG_INVALID_MAX_VALUE);
	}

	this.maxValue = maxValue;
    }

    /**
     * Overrides the protected <code>createDefaultModel()</code> in {@link JTextField}.
     * 
     * @return Returns the {@link NumericDocument} implementation for the {@link Document} interface which provides the
     *         functionality described in the class header.
     * 
     */
    protected Document createDefaultModel() {
	return new NumericDocument();
    }

    /**
     * Returns the text in the text field.
     * 
     * @return The text in the text field.
     */
    String getJTextFieldText() {

	return super.getText();
    }

    /**
     * Determines if the class is empty or not.
     * 
     * @return true if the current text is null, or white space string or an empty string. Returns false other wise.
     */
    public boolean isEmpty() {

	if (super.getText() == null) {
	    return true;
	} else if (super.getText().trim().isEmpty()) {
	    return true;
	} else {
	    return false;
	}
    }

    /**
     * Extends the {@link PlainDocument} which is the used by default by the {@link JTextField}. This class overrides
     * the behavior of the {@link PlainDocument#insertString(int, String, AttributeSet)} function which is called when
     * ever a new string is inserted in the text field.
     * 
     */
    class NumericDocument extends PlainDocument {

	/**
	 * 
	 */
	private final static String NUMERIC_EXPRESSION = "^[1-9]{1,1}[0-9]{0,}$";

	/**
	 * 
	 */
	Pattern pattern = Pattern.compile(NUMERIC_EXPRESSION);

	/**
	 * 
	 */
	private static final long serialVersionUID = 6171569384656545896L;

	public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {

	    StringBuilder stringBuilder = new StringBuilder(getJTextFieldText());
	    stringBuilder.insert(offs, str);
	    String probableValue = stringBuilder.toString();

	    Matcher matcher = pattern.matcher(probableValue);

	    int probableIntegerValue = 0;

	    if (matcher.find() && matcher.start() == 0) {

		try {
		    probableIntegerValue = Integer.parseInt(probableValue);

		    if (probableIntegerValue <= maxValue) {
			super.insertString(offs, str, a);
		    }
		} catch (NumberFormatException ex) {
		    return;
		}
	    } else {
		return;
	    }
	}
    }
}
